package com.nulldev.util.settings;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;

import com.nulldev.util.io.FileUtil;
import com.nulldev.util.java.JVMUtil;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.math.MathUtil;
import com.nulldev.util.other.ArrayUtils;
import com.nulldev.util.uri.URIUtil;

public class Preferences {

	/*
	 * File Format
	 * key: value<newline>
	 * isBadCode: true<newline>
	 * ### comment <-- not read
	 * 
	 * readOption(key) -> value
	 * readOptionBoolean(isBadCode) -> Boolean.TRUE
	 * 
	 */
	
	public static boolean preferencesDisabled = JVMUtil.isArg("--disable-settings");
	private static final Logger log = LoggerFactory.getLogger(true, true);
	public static final String DEFAULT_VALUE = "value.default";
	public static final String HOME_DIRECTORY = System.getProperty("user.home");
	
	private String[] data = new String[0];
	private final File file;
	private URL url;
	private String file_data = "";
	
	public Preferences(final File file) {
		this.file = file;
		if (file == null || file.isDirectory()) {
			return;
		}
		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (final IOException e) {
				log.error("Failed to create preferences. [file.create() -> " + file.toString() + "]", e);
				return;
			}
		}
		try {
			final String[] data_pre = (this.file_data = FileUtil.readFileToString(file)).replaceAll(JVMUtil.LINE_SEP_WINDOWS, "").replaceAll(JVMUtil.LINE_SEP_BASIC, "").split("<newline>");
			this.data = data_pre;
		}catch (final Exception e) {
			log.error("Failed to load preferences.", e);
		}
	}
	
	public Preferences(final URL url) {
		this.file = null;
		this.url = url;
		try {
			final String[] data_pre = (this.file_data = URIUtil.downloadPage(url)).replaceAll(JVMUtil.lineSep(), "").split("<newline>");
			this.data = data_pre;
		}catch (final Exception e) {
			log.error("Failed to load preferences.", e);
		}
	}
	
	private void reload() {
		try {
			if (this.file != null) {
				final String[] data_pre = (this.file_data = FileUtil.readFileToString(file)).replaceAll(JVMUtil.LINE_SEP_WINDOWS, "").replaceAll(JVMUtil.LINE_SEP_BASIC, "").split("<newline>");
				this.data = data_pre;
			}else if (this.url != null) {
				final String[] data_pre = (this.file_data = URIUtil.downloadPage(url)).replaceAll(JVMUtil.lineSep(), "").split("<newline>");
				this.data = data_pre;
			}
		}catch (final Exception e) {
			log.error("Failed to load preferences.", e);
		}
	}
	
	public HashMap<String, String> readAll(){
		final HashMap<String, String> out = new HashMap<String, String>();
		if (this.data.length > 0) {
			for (final String val : this.data) {
				try {
					final String[] d = val.split(": ");
					out.put(d[0], ArrayUtils.StringArrayUtils.addFrom(d, 1));
				}catch (Exception e) {}
			}
		}
		return out;
	}
	
	public String readOption(final String key) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				final String[] d = val.split(": ");
				if (d[0].equals(key)) {
					return ArrayUtils.StringArrayUtils.addFrom(d, 1);
				}
			}
		}
		return DEFAULT_VALUE;
	}
	
	public boolean isKey(final String key) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				if (!val.startsWith("###")) {
					final String[] d = val.split(": ");
					if (d[0].equals(key)) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	public String readOption(final String key, final String fallback) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				if (!val.startsWith("###")) {
					final String[] d = val.split(": ");
					if (d[0].equals(key)) {
						return ArrayUtils.StringArrayUtils.addFrom(d, 1);
					}
				}
			}
		}
		return fallback;
	}
	
	public int readOptionInt(final String key, final int fallback) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				if (!val.startsWith("###")) {
					final String[] d = val.split(": ");
					if (d[0].equals(key)) {
						return MathUtil.safeParseI(d[1], fallback);
					}
				}
			}
		}
		return fallback;
	}
	
	public boolean readOptionBoolean(final String key) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				if (!val.startsWith("###")) {
					final String[] d = val.split(": ");
					if (d[0].equals(key)) {
						try {
							return Boolean.parseBoolean(d[1]);
						}catch (Exception e) {
							return false;
						}
					}
				}
			}
		}
		return false;
	}
	
	public boolean readOptionBoolean(final String key, final boolean fallback) {
		if (this.data.length > 0) {
			for (final String val : this.data) {
				if (!val.startsWith("###")) {
					final String[] d = val.split(": ");
					if (d[0].equals(key)) {
						try {
							return Boolean.parseBoolean(d[1]);
						}catch (Exception e) {
							return fallback;
						}
					}
				}
			}
		}
		return fallback;
	}
	
	public void printAll() {
		synchronized (System.err) {
			synchronized (System.out) {
				ArrayUtils.printLln(this.data);
			}
		}
	}
	
	public boolean fileContains(final String data) {
		return this.file_data.contains(data);
	}
	
	public void reset() {
		if (this.preferencesDisabled) {
			return;
		}
		try {
			FileUtil.writeFile(this.file, "", false);
			this.reload();
		} catch (final Exception e) {
			log.error("Failed to reset preferences.", e);
		}
	}
	
	public void writeComment(final Object object) {
		if (this.preferencesDisabled) {
			return;
		}
		if (this.file == null && this.url != null) {
			log.error("Preferences was loaded from a URL, write is not allowed.");
			return;
		}
		if (this.file == null && this.url == null) {
			log.error("Both data sources were null, stopping!");
			return;
		}
		try {
			boolean exists = false;
			for (final String dta : this.data) {
				final String[] d = dta.split(": ");
				if (d[0].equals("### " + object)) {
					exists = true;
					break;
				}
			}
			if (exists) {
				final StringBuilder out = new StringBuilder();
				for (final String dta : this.data) {
					final String[] d = dta.split(": ");
					if (d[0].equals("### " + object)) {
						out.append(d[0] + String.valueOf(object) + "<newline>" + JVMUtil.LINE_SEP_WINDOWS);
					}else {
						out.append(dta + "<newline>" + JVMUtil.LINE_SEP_WINDOWS);
					}
				}
				FileUtil.writeFile(this.file, out.toString(), true);
			}else {
				if (this.data.length == 0) {
					FileUtil.writeFile(this.file, "### " + String.valueOf(object) + "<newline>" + JVMUtil.LINE_SEP_WINDOWS, true);
				}else {
					FileUtil.writeFile(this.file, this.file_data + "### " + String.valueOf(object) + "<newline>" + JVMUtil.LINE_SEP_WINDOWS, true);
				}
			}
//			if (this.data.length == 0) {
//				FileUtil.writeFile(this.file, key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.lineSep(), true);
//			}else {
//				FileUtil.writeFile(this.file, this.file_data + key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.lineSep(), true);
//			}
			this.reload();
		} catch (final Exception e) {
			log.error("Failed to write preferences.", e);
		}
	}
	
	public void writeOption(final String key, final Object object) {
		if (this.preferencesDisabled) {
			return;
		}
		if (this.file == null && this.url != null) {
			log.error("Preferences was loaded from a URL, write is not allowed.");
			return;
		}
		if (this.file == null && this.url == null) {
			log.error("Both data sources were null, stopping!");
			return;
		}
		try {
			boolean exists = false;
			for (final String dta : this.data) {
				final String[] d = dta.split(": ");
				if (d[0].equals(key)) {
					exists = true;
					break;
				}
			}
			if (exists) {
				final StringBuilder out = new StringBuilder();
				for (final String dta : this.data) {
					final String[] d = dta.split(": ");
					if (d[0].equals(key)) {
						out.append(d[0] + ": " + String.valueOf(object) + "<newline>" + JVMUtil.LINE_SEP_WINDOWS);
					}else {
						out.append(dta + "<newline>" + JVMUtil.LINE_SEP_WINDOWS);
					}
				}
				FileUtil.writeFile(this.file, out.toString(), true);
			}else {
				FileUtil.writeFile(this.file, this.file_data + key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.LINE_SEP_WINDOWS, true);
//				if (this.data.length == 0) {
//					FileUtil.writeFile(this.file, key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.lineSep(), true);
//				}else {
//					
//				}
			}
//			if (this.data.length == 0) {
//				FileUtil.writeFile(this.file, key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.lineSep(), true);
//			}else {
//				FileUtil.writeFile(this.file, this.file_data + key + ": " + String.valueOf(object) + "<newline>" + JVMUtil.lineSep(), true);
//			}
			this.reload();
		} catch (final Exception e) {
			log.error("Failed to write preferences.", e);
		}
	}
}
